package org.example.servlet;

import com.alibaba.fastjson.JSON;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.http.HttpStatus;
import org.example.pojo.FileInfo;
import org.example.service.FileInfoService;
import org.example.service.impl.FileInfoServiceImpl;
import org.example.utils.AESUtils;
import org.example.utils.Base64Utils;
import org.example.utils.RSAUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;


/**
*@Description   servlet服务连接设计
*@Author  24025
*@Date  2022/2/18
*/
public class FileServlet extends BaseServlet {

    /**
     * 保存文件的磁盘路径，windows系统存在C盘
     */
    public static final String DISKPATH = "E:\\http-file";

    /**
     * RSC 公钥
     */
    public static final String PUB_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCB10JzCwS4LXMihfDjaKhZHatNO+e6MCTzEZFp68miKNov5MsyNVlqPO2JAb8uJb3eZGoqWe8iaEX/xgLWJ8+uEKz4jFMBToeTqjx1gtGMRAgxtGpB61kDZDL2aVUfXfipndxFOdKQF9JikfN1RbK1aH0C54RHpfpDS9kI1CEKCQIDAQAB";

    /**
     * 注入业务层
     */
    private final FileInfoService fileInfoService = new FileInfoServiceImpl();

    /**
     * 接收客户端上传的文件，并返回文件的uuid
     */
    public void upload(HttpServletRequest request, HttpServletResponse response) {

        // 客户端验证
        boolean isClient = clientVerify(request);
        System.out.println("是不是客户端：" + isClient);
        if (!isClient) {
            response.setStatus(HttpStatus.FORBIDDEN_403);
            System.out.println("403返回");
            return;
        }
        response.setContentType("text/plain;charset=utf-8");

        // 保存客户端传来的文件流到本地
        if (ServletFileUpload.isMultipartContent(request)) {

            FileItemFactory fileItemFactory = new DiskFileItemFactory();
            ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
            RequestContext requestContext = new ServletRequestContext(request);

            try {
                List<FileItem> fileItemList = servletFileUpload.parseRequest(requestContext);
                for (FileItem fileItem : fileItemList) {
                    if (fileItem.isFormField()) {
                        System.out.println("表单项的 name 属性值：" + fileItem.getFieldName());
                        System.out.println("表单项的 value 属性值：" + fileItem.getString("UTF-8"));
                    } else {
                        // 原始文件名
                        String fileOriName = fileItem.getName();
                        // 文件的类型
                        String fileType = fileOriName.substring(fileOriName.lastIndexOf("."));
                        // 文件的大小
                        long fileSize = fileItem.getSize();

                        // UUID重命名
                        String uuid = UUID.randomUUID().toString();
                        String uuidName = uuid + fileType;

                        // 创建yyyyMMdd格式的文件夹
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
                        String datePath = dateFormat.format(new Date());
                        // 生成文件的保存目录
                        File savePath = new File(DISKPATH + "\\" + datePath);
                        if (!savePath.exists()) {
                            savePath.mkdir();
                        }

                        // 生成 AES 随机密钥
                        String aesKey = AESUtils.getSecretKey();
                        System.out.println("AES密钥：" + aesKey);
                        // 使用 RSA 公钥对 AES 密钥 加密
                        byte[] data = RSAUtils.encryptByPublicKey(aesKey.getBytes(), PUB_KEY);
                        // 文件的数字信封
                        String envelope = Base64Utils.encode(data);

                        // 加密文件，并保存到磁盘
                        AESUtils.encryptFile(aesKey, fileItem.getInputStream(), savePath.toString() + "\\" + uuidName);

                        // 文件的创建时间
                        Timestamp createTime = new Timestamp(System.currentTimeMillis());
                        // 保存文件的元数据到数据库
                        FileInfo fileInfo = new FileInfo(fileOriName, fileSize, fileType, createTime, savePath.toString() + "\\" + uuidName, envelope, uuid);
                        int rows = fileInfoService.addOneFileInfo(fileInfo);

                        PrintWriter writer = response.getWriter();
                        writer.write(uuid);
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
    }

    /**
     * 通过uuid下载文件n
     */
    public void downloadByUuid(HttpServletRequest request, HttpServletResponse response) {

        // 客户端验证
        boolean isClient = clientVerify(request);
        System.out.println("是不是客户端：" + isClient);
        if (!isClient) {
            response.setStatus(HttpStatus.FORBIDDEN_403);
            return;
        }

        // 通过 http 传输文件到客户端
        try {
            String uuid = request.getParameter("uuid");
            FileInfo fileInfo = fileInfoService.getOneFileInfoByUuid(uuid);
            if (fileInfo == null) {
                response.setStatus(HttpStatus.NOT_FOUND_404);
                return;
            }

            // 文件在服务器的路径
            String savePath = fileInfo.getSavePath();
            // 文件的原始名
            String fileName = fileInfo.getFileOriName();
            // 数字信封
            String envelope = fileInfo.getEnvelope();

            ServletContext servletContext = getServletContext();
            String mimeType = servletContext.getMimeType(savePath);
            response.setContentType(mimeType);

            // 配置响应头
            response.setHeader("Content-Disposition", "attachment; fileName=" + fileName);
            response.addHeader("envelope", envelope);

            // 返回文件流
            InputStream inputStream = new FileInputStream(savePath);
            OutputStream outputStream = response.getOutputStream();
            IOUtils.copy(inputStream, outputStream);
        } catch (IOException e) {
            response.setStatus(HttpStatus.GONE_410);
        }
    }

    /**
     * 通过uuid获取文件元数据信息
     */
    public void getFileInfoByUuid(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // 客户端验证
        boolean isClient = clientVerify(request);
        System.out.println("是不是客户端：" + isClient);
        if (!isClient) {
            response.setStatus(HttpStatus.FORBIDDEN_403);
            return;
        }

        // 获得 uuid
        String uuid = request.getParameter("uuid");
        FileInfo fileInfo = fileInfoService.getOneFileInfoByUuid(uuid);
        // 资源未找到
        if (fileInfo == null) {
            response.setStatus(HttpStatus.NOT_FOUND_404);
            return;
        }

        // 返回文件信息的json
        response.setContentType("text/json; charset=utf-8");
        String json = JSON.toJSONString(fileInfo);
        System.out.println(json);
        PrintWriter writer = response.getWriter();
        writer.write(json);
    }

    /**
     * 最近上传的 10 个文件的元数据 Json 数组
     */
    public void getFileInfoList(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // 客户端验证
        boolean isClient = clientVerify(request);
        System.out.println("是不是客户端：" + isClient);
        if (!isClient) {
            response.setStatus(HttpStatus.FORBIDDEN_403);
            return;
        }

        // 查询最近上传的文件的元数据列表
        List<FileInfo> fileInfos = fileInfoService.getFileInfoList();

        // 返回文件信息的json
        String json = JSON.toJSONString(fileInfos);
        PrintWriter writer = response.getWriter();
        writer.write(json);
    }

    /**
     * 客户端身份验证
     */
    private boolean clientVerify(HttpServletRequest request) {

        // 获取X-SID、X-Signature，并验证签名
        String sid = request.getHeader("X-SID");
        String signature = request.getHeader("X-Signature");
        System.out.println("X-SID:" + sid);
        System.out.println("X-Signature:" + signature);

        try {
            return RSAUtils.verify(sid.getBytes(), PUB_KEY, signature);
        } catch (Exception exception) {
            return false;
        }
    }
}
